package com.listeningframework.boot.autoconfigure.security.overrides;

import com.listeningframework.boot.autoconfigure.security.userdetails.SecurityMenus;
import com.listeningframework.boot.autoconfigure.security.userdetails.SecurityMenusAccordion;
import com.listeningframework.boot.autoconfigure.security.userdetails.SecurityMenusAccordionTree;
import com.listeningframework.boot.autoconfigure.security.userdetails.SecurityUserDetails;
import com.listeningframework.boot.constants.YesNo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author lixining
 * @version $Id: ListeningJdbcDaoImpl.java, v 0.1 2015年8月13日 下午5:11:57 lixining
 */
public class CustomJdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
    /**
     * RBAC角色
     */
    private static final String ROLE_RBAC = "ROLE_RBAC";
    /**
     * Role Prefix
     */
    private String rolePrefix = "";

    /**
     * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(String)
     */
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<SecurityUserDetails> users = loadUsersByUsername(username);
        if (users.size() == 0) {
            throw new UsernameNotFoundException("user[" + username + "] not found");
        }
        SecurityUserDetails user = users.get(0);
        Set<GrantedAuthority> dbAuthsSet = new HashSet<>();
        dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername()));
        if (StringUtils.equalsIgnoreCase(user.getUserId(), ROLE_RBAC)) {
            dbAuthsSet.add(new SimpleGrantedAuthority(ROLE_RBAC));
        }

        List<GrantedAuthority> dbAuths = new ArrayList<>(dbAuthsSet);
        // if (CollectionUtils.isEmpty(dbAuths)) {
        // throw new UsernameNotFoundException("User[" + username + "] has no
        // GrantedAuthority");
        // }
        user.setAuthorities(dbAuths);
        user.setSecurityMenus(loadSecurityMenus(username));
        return user;
    }

    /**
     * Get Security User Menus
     *
     * @param username 账号
     * @return SecurityMenus
     */
    private SecurityMenus loadSecurityMenus(String username) {
        SecurityMenus securityMenu = new SecurityMenus();
        List<SecurityMenusAccordion> accordionList = loadSecurityAccordion(username);
        securityMenu.setSecurityAccordionList(accordionList);
        if (CollectionUtils.isEmpty(accordionList)) {
            return securityMenu;
        }
        for (SecurityMenusAccordion accordion : accordionList) {
            String accordionId = accordion.getId();
            securityMenu.addTreeData(accordionId, loadSecurityAccordionTreeNodes(accordionId, username));
        }
        return securityMenu;
    }

    /**
     * Load Security Accordion Menus
     *
     * @param username 账号
     * @return List
     */
    private List<SecurityMenusAccordion> loadSecurityAccordion(String username) {
        return getJdbcTemplate().query(getMenuSQL(), new String[]{"ROOT", username}, (rs, rowNum) -> {
            SecurityMenusAccordion accordion = new SecurityMenusAccordion();
            accordion.setId(rs.getString("ID"));
            accordion.setText(rs.getString("TEXT"));
            accordion.setIconCls(rs.getString("ICONCLS"));
            return accordion;
        });
    }

    /**
     * @param parentId 父节点
     * @param username 用户名
     * @return List
     */
    private List<SecurityMenusAccordionTree> loadSecurityAccordionTreeNodes(String parentId, String username) {
        return getJdbcTemplate().query(getMenuSQL(), new String[]{parentId, username}, (rs, rowNum) -> {
            SecurityMenusAccordionTree treeNode = new SecurityMenusAccordionTree();
            treeNode.setId(rs.getString("ID"));
            treeNode.setIconCls(rs.getString("ICONCLS"));
            treeNode.setParentId(parentId);
            treeNode.setText(rs.getString("TEXT"));
            treeNode.setUrl(rs.getString("URL"));
            treeNode.setChildren(loadSecurityAccordionTreeNodes(rs.getString("ID"), username));
            return treeNode;
        });
    }

    private String getMenuSQL() {
        StringBuilder buffer = new StringBuilder();
        buffer.append(" SELECT DISTINCT D.ID           ID,");
        buffer.append("                 D.TEXT         TEXT,");
        buffer.append("                 D.ICON_CLS     ICONCLS,");
        buffer.append("                 D.URL          URL,");
        buffer.append("                 D.PARENT_ID    PARENTID,");
        buffer.append("                 D.DISPLAY      DISPLAY");
        buffer.append("    FROM LS_RBAC_USERS A, LS_RBAC_USER_ROLE B, LS_RBAC_ROLE_MENU C, LS_RBAC_MENUS D");
        buffer.append(" WHERE A.ID = B.USER_ID AND B.ROLE_ID = C.ROLE_ID AND C.MENU_ID = D.ID");
        buffer.append("       AND D.PARENT_ID = ? AND A.USER_NAME = ? ORDER BY D.DISPLAY ASC");
        return buffer.toString();
    }

    /**
     * Executes the SQL <tt>usersByUsernameQuery</tt> and returns a list of
     * UserDetails objects. There should normally only be one matching user.
     *
     * @param username 用户名
     * @return List
     */
    protected List<SecurityUserDetails> loadUsersByUsername(String username) {
        StringBuilder buffer = new StringBuilder();
        buffer.append(" SELECT ID               ID,");
        buffer.append("        USER_NAME        USERNAME,");
        buffer.append("        REAL_NAME        REALNAME,");
        buffer.append("        MOBILE           MOBILE,");
        buffer.append("        EMAIL            EMAIL,");
        buffer.append("        PASSWORD         PASSWORD,");
        buffer.append("        STATUS           STATUS,");
        buffer.append("        CREATED_BY       CREATEDBY,");
        buffer.append("        CREATED_DATE     CREATEDDATE,");
        buffer.append("        UPDATED_BY       UPDATEDBY,");
        buffer.append("        UPDATED_DATE     UPDATEDDATE");
        buffer.append(" FROM LS_RBAC_USERS WHERE USER_NAME = ?");

        return getJdbcTemplate().query(buffer.toString(), new String[]{username}, (rs, rowNum) -> {
            String username1 = rs.getString("USERNAME");
            String password = rs.getString("PASSWORD");
            String realName = rs.getString("REALNAME");
            String mobile = rs.getString("MOBILE");
            String email = rs.getString("EMAIL");
            String userId = rs.getString("ID");
            String status = rs.getString("STATUS");
            SecurityUserDetails securityUserDetails = new SecurityUserDetails();
            securityUserDetails.setUsername(username1);
            securityUserDetails.setPassword(password);
            securityUserDetails.setMobile(mobile);
            securityUserDetails.setEmail(email);
            securityUserDetails.setRealName(realName);
            securityUserDetails.setEnabled(YesNo.isYes(status));
            securityUserDetails.setUserId(userId);
            return securityUserDetails;
        });
    }

    /**
     * Loads authorities by executing the SQL from
     * <tt>groupAuthoritiesByUsernameQuery</tt>.
     *
     * @param username 用户名
     * @return List
     */
    private List<GrantedAuthority> loadGroupAuthorities(String username) {
        StringBuilder buffer = new StringBuilder();
        buffer.append(" SELECT  DISTINCT D.ID                    ID,");
        buffer.append("                  D.PERMISSION_CODE       PERMISSIONCODE,");
        buffer.append("                  D.PERMISSION_NAME       PERMISSIONNAME");
        buffer.append(" FROM LS_RBAC_USERS A,");
        buffer.append("      LS_RBAC_USER_ROLE B,");
        buffer.append("      LS_RBAC_ROLE_PERMISSION C,");
        buffer.append("      LS_RBAC_PERMISSIONS D");
        buffer.append(" WHERE A.ID = B.USER_ID");
        buffer.append("       AND B.ROLE_ID = C.ROLE_ID");
        buffer.append("       AND C.PERMISSION_ID = D.ID");
        buffer.append("       AND A.USER_NAME = ?");

        return getJdbcTemplate().query(buffer.toString(), new String[]{username}, (rs, rowNum) -> {
            String roleName = getRolePrefix() + rs.getString("PERMISSIONCODE");
            return new SimpleGrantedAuthority(roleName);
        });
    }

    public void setRolePrefix(String rolePrefix) {
        this.rolePrefix = rolePrefix;
    }

    protected String getRolePrefix() {
        return rolePrefix;
    }
}
